#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]){
    /* Usados para iteracoes */
    int i, j;

    /* Ordem da Matriz. Ex: 3 */
    int jOrder;

    /* Fila para ser avaliada. Ex: 2 */
    int jRowTest;

    /* Erro permitido. Ex: 0.001 */
    float jError;

    /* Numero maximo de iteracoes. Ex: 20000 */
    int jIteMax;

    /* Matriz A e vetor B, onde A tem ordem J_ORDEM e B tem tamanho J_ORDEM */
    float *A, *Ae, *B, *Be, *X, *Dif;

    /* Vetor auxiliar que sera reutilizado no fim do metodo */
    float *somatorio;

    /* Auxiliares para calculo do Erro */
    float maxDif, maxX, absX;

    /* Erro */
    float MR;

    /* Verifica o valor */
    float result;
    /* Contador de iteracoes */
    int numeroIter = 0;

  	/* Arquivo de entrada de leitura */
	FILE* fpin;
    if(argv[1] != NULL){
        char buffer[25];
        strcpy(buffer,"");
        int order = atoi(argv[1]);
        sprintf(buffer, "matriz%d.txt", order);
        fpin = fopen(buffer, "r");
    }else
        fpin = fopen("matriz3.txt","r");

	if (fpin == NULL)
	{
        printf("Erro na abertura do arquivo!\n");
		getchar();
		exit(1);
    }

    fscanf(fpin, "%d",  &jOrder);
    fscanf(fpin, "%d",  &jRowTest);
    fscanf(fpin, "%f", &jError);
    fscanf(fpin, "%d",  &jIteMax);

    /* Alocacao de memoria para a matriz A, B e X */
    somatorio = (float *) malloc(jOrder*sizeof(float));
    Dif = (float *) malloc(jOrder*sizeof(float));
    X  = (float *) malloc(jOrder*sizeof(float));
    B  = (float *) malloc(jOrder*sizeof(float));
	Be = (float *) malloc(jOrder*sizeof(float));
    A  = (float *) malloc(jOrder*jOrder*sizeof(float*));
	Ae = (float *) malloc(jOrder*jOrder*sizeof(float*));

    printf("Input file was read.\nMatrix Order: %d\nRow Test: %d\nError: %f\nMaximum Number of Iterations: %d\n", jOrder, jRowTest, jError, jIteMax);

    /* Leitura da matriz A */
    for (i = 0; i < jOrder; i++){
        for (j = 0; j < jOrder; j++){
            fscanf(fpin, "%f", &A[((i*jOrder) + j)]);
		}
    }
    /* Leitura da matriz B */
    for (i = 0; i < jOrder; i++){
        fscanf(fpin, "%f", &B[i]   );
    }

    /* Preparando a matriz A(*) */
    float diagonal;
	for (i = 0; i < jOrder; i++)
	{
		diagonal = A[((i*jOrder) + i)];
		Be[i] = B[i]/diagonal;
        for (j = 0; j < jOrder; j++)
		{
			if (j == i)
			{
				Ae[((i*jOrder) + j)] = 0.0;
			}
			else
			{
				Ae[((i*jOrder) + j)] = A[((i*jOrder) + j)]/diagonal;
			}
		}
    }
    /**********************************************************************/
    /* Inicia as iteracoes */

    /* Iteracao 0: */
    for(i=0; i< jOrder; i++)
        X[i] = Be[i];

    for(numeroIter = 1; numeroIter < jIteMax; numeroIter++){
        for(i = 0; i < jOrder; i++){
            somatorio[i] = 0;
            for(j = 0; j < jOrder; j++){
                if(i != j)
                    somatorio[i] += (Ae[((i*jOrder) + j)]*X[j]);
            }
        }
        /* Aqui ja comeca o calculo do MR */
        maxX = maxDif = 0;
        for(i=0; i < jOrder; i++){
            Dif[i] = Be[i] - somatorio[i] - X[i];
            Dif[i] = Dif[i] < 0 ? -Dif[i] : Dif[i]; //absolute value
            X[i] = Be[i] - somatorio[i];
            absX = X[i] < 0 ? -X[i] : X[i]; //absolute value
            maxX = absX > maxX ? absX : maxX;
            maxDif = Dif[i] > maxDif ? Dif[i] : maxDif;
        }
        MR = maxDif/maxX;
        if(MR <= jError){
            printf("Criterio de parada atingido: Mr: %f <= %f\n", MR, jError);
            //printf("Numero de iteracoes: %d\n", numeroIter);
            break;
        }
    }

    /* Saida padrao: */
    result = 0;
    for( i = 0; i < jOrder; i++){
        result += A[((jRowTest*jOrder) + i)]*X[i];
    }
    printf("Iterations: %d\n", numeroIter);
    printf("RowTest: %d => [%f] =? %f\n", jRowTest, result, B[jRowTest]);

    /* Soh imprime no fim pra ver se deu certo. */
	/*for (i = 0; i < jOrder; i++)
	{
        printf("X[%d] = %f\n", i, X[i]);
    }*/


    /*********************************************************************/
    /* Fechando o arquivo */
    fclose(fpin);

    /* Liberando memoria da martiz A */
    free(A);

    /* Liberando memoria da matriz B */
    free(B);

    /* Liberando memoria das matrizes A(*) e B(*) */
	free(Ae);

	free(Be);

    free(X);

    free(Dif);

    return 0;
}

